package com.bolt.report.business.modules.view;

import com.bolt.common.convert.Convert;
import com.bolt.common.reflect.ObjectUtil;
import com.bolt.common.utils.DateUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.common.web.RequestUtil;
import com.bolt.convention.data.Record;
import com.bolt.convention.data.ResultMessage;
import com.bolt.convention.data.Results;
import com.bolt.core.view.components.Form;
import com.bolt.report.business.ReportDBDefManager;
import com.bolt.report.business.modules.dataset.dto.converter.ReportDataSetConverter;
import com.bolt.report.business.modules.dataset.entity.ReportDataSetEntity;
import com.bolt.report.business.modules.dataset.service.ReportDataSetService;
import com.bolt.report.definition.*;
import com.bolt.report.engine.BRDataSource;
import com.bolt.report.engine.ObjectFactory;
import com.bolt.report.engine.ReportConstants;
import com.bolt.report.engine.ReportDefManager;
import com.bolt.report.engine.export.SimpleExporterInput;
import com.bolt.report.engine.export.SimpleStreamExporterOutput;
import com.bolt.report.engine.export.SimpleWriterExporterOutput;
import com.bolt.report.engine.export.excel.ExcelExporter;
import com.bolt.report.engine.export.html.HtmlExporter;
import com.bolt.report.engine.export.pdf.PdfExporter;
import com.bolt.report.engine.fill.ReportFiller;
import com.bolt.report.engine.model.ReportPrint;
import com.bolt.report.engine.query.QueryExecute;
import com.bolt.report.engine.query.QueryExecuteFactory;
import com.bolt.report.exception.ReportException;
import com.bolt.report.vo.PrintPaperVo;
import com.bolt.support.freemarker.DirectiveUtil;
import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

@Controller()
@RequestMapping({"/report"})
@Slf4j
public class ReportViewController {
    @Resource(name = "dbReportDefManager")
    private ReportDefManager reportManager;

    @Resource(name = "reportFreeMarkerConfig")
    private Configuration cfg;


    @Autowired
    private ReportDataSetService reportDataSetService;


    @RequestMapping({"/view/{reportId}"})
    public void view(@PathVariable String reportId, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StrUtil.isBlank(reportId)) {
            throw new ReportException("报表文件不存在");
        }
        try {
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter output = response.getWriter();
            ReportDefinition reportDefinition = reportManager.loadReportDefinition(reportId);
            SimpleHash simpleHash = DirectiveUtil.buildTemplateModel(cfg, request, response);
            simpleHash.put("base", request.getContextPath());
            simpleHash.put("reportId", reportId);
            simpleHash.put( ReportConstants.BASE_URL,  RequestUtil.getBaseUrl(request));
            buildQueryForm(reportDefinition, request, simpleHash);
            Template template = cfg.getTemplate(ReportConstants.REPORT_VIEW_TEMPLATE_NAME);
            template.process(simpleHash, output);
        } catch (Exception ex) {
            log.error("view error", ex);
        }
    }

    @RequestMapping({"/pageContent"})
    @ResponseBody
    public ResultMessage expHtml(HttpServletRequest request, HttpServletResponse response) {
        String reportId = request.getParameter(ReportConstants.REPORT_ID_PARAM);
        if (StrUtil.isBlank(reportId)) {
            throw new ReportException("报表文件不存在");
        }
        Map<String, Object> param = RequestUtil.getRequestMap(request);
        param.put(ReportConstants.BASE_URL, RequestUtil.getBaseUrl(request));
        ReportDefinition reportDefinition = reportManager.loadReportDefinition(reportId);
        ReportFiller filler = ObjectFactory.createFiller(reportDefinition);
        ReportPrint reportPrint = filler.fill(param);

        Integer pageIndex = Convert.toInt(request.getParameter("pageIndex"), 0);
        String reportHtml = pageContent(reportPrint, pageIndex);
        Record data = new Record();


        data.put("pageContent", reportHtml);
        data.put("pageSize", reportPrint.getPages().size());
        data.put("pageIndex", pageIndex);
        return Results.success(data);
    }


    public String pageContent(ReportPrint reportPrint, int pageIndex) {


        HtmlExporter htmlExporter = new HtmlExporter();
        htmlExporter.setExporterInput(new SimpleExporterInput(reportPrint));
        StringBuffer htmlBuffer = new StringBuffer();
        htmlExporter.setExporterOutput(new SimpleWriterExporterOutput(htmlBuffer));
        htmlExporter.setParameter(ReportConstants.PAGE_INDEX, pageIndex);
        htmlExporter.exportReport();
        return htmlBuffer.toString();

    }

    private void buildQueryForm(ReportDefinition reportDefinition, HttpServletRequest request, SimpleHash simpleHash) {
        Form form = reportDefinition.getQueryForm();
        if (ObjectUtil.isNotNull(form) && form.getGroup().size()>0) {
            String reportUrl = RequestUtil.getBaseUrl(request) + "report/view?reportId=" + reportDefinition.getId();
            form.setQueryAction(reportUrl);
            simpleHash.put("tag", form);
        }
    }


    @RequestMapping({"/design"})
    public void design(HttpServletRequest request, HttpServletResponse response) throws IOException {

        try {
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter output = response.getWriter();
            Map<String, Object> dataParam = new HashMap<>();
            dataParam.put("base", request.getContextPath());
            Template template = cfg.getTemplate(ReportConstants.REPORT_DESIGN_TEMPLATE_NAME);
            template.process(dataParam, output);
        } catch (Exception ex) {
            // logger.error(ex);
        }
    }

    @RequestMapping({"/loadHtmlPrintPaper/{reportId}"})
    @ResponseBody
    public ResultMessage loadPrintPaper(@PathVariable String reportId,HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StrUtil.isBlank(reportId)) {
            throw new ReportException("报表文件不存在");
        }
        Map<String, Object> param = RequestUtil.getRequestMap(request);
        ReportDefinition reportDefinition = reportManager.loadReportDefinition(reportId);
        ReportFiller filler = ObjectFactory.createFiller(reportDefinition);
        ReportPrint reportPrint = filler.fill(param);
        HtmlExporter htmlExporter = new HtmlExporter(param);
        htmlExporter.setExporterInput(new SimpleExporterInput(reportPrint));
        StringBuffer htmlBuffer = new StringBuffer();
        htmlExporter.setExporterOutput(new SimpleWriterExporterOutput(htmlBuffer));
        htmlExporter.exportReport();
        PrintPaperVo printPaper = new PrintPaperVo();
        printPaper.setPaperType(reportPrint.getPaper().getPaperType().name());
        printPaper.setPageWidth(reportPrint.getPageWidth(PaperSize.SizeUnit.MM));
        printPaper.setPageHeight(reportPrint.getPageHeight(PaperSize.SizeUnit.MM));
        printPaper.setTopMargin(reportPrint.getTopMargin());
        printPaper.setBottomMargin(reportPrint.getBottomMargin());
        printPaper.setLeftMargin(reportPrint.getLeftMargin());
        printPaper.setRightMargin(reportPrint.getRightMargin());
        printPaper.setPrintOrder(PrintOrder.VERTICAL.equals(reportPrint.getPrintOrderValue()) ? "portrait" : "landscape");
        printPaper.setContent(htmlBuffer.toString());
        return Results.success(printPaper);
    }

    @RequestMapping({"/pdf/{reportId}"})
    public void pdf(@PathVariable String reportId,HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StrUtil.isBlank(reportId)) {
            throw new ReportException("报表文件不存在");
        }
        Map<String, Object> param = RequestUtil.getRequestMap(request);
        boolean forPrint = RequestUtil.getQueryBool(request, "forPrint", false);
        ReportDefinition reportDefinition = reportManager.loadReportDefinition(reportId);
        ReportFiller filler = ObjectFactory.createFiller(reportDefinition);
        ReportPrint reportPrint = filler.fill(param);
        String fileName = buildFileName(reportDefinition, "pdf");
        if (forPrint) {
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "inline;filename=\"" + fileName + "\"");
        } else {
            response.setContentType("application/octet-stream;charset=ISO8859-1");
            response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
        }
        PdfExporter pdfExporter = new PdfExporter(param);
        pdfExporter.setExporterInput(new SimpleExporterInput(reportPrint));
        pdfExporter.setExporterOutput(new SimpleStreamExporterOutput(response.getOutputStream()));
        pdfExporter.exportReport();
    }

    @RequestMapping({"/excel/{reportId}"})
    @ResponseBody
    public void excel(@PathVariable String reportId,HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (StrUtil.isBlank(reportId)) {
            throw new ReportException("报表文件不存在");
        }
        Map<String, Object> param = RequestUtil.getRequestMap(request);
        ReportDefinition reportDefinition = reportManager.loadReportDefinition(reportId);
        ReportFiller filler = ObjectFactory.createFiller(reportDefinition);
        ReportPrint reportPrint = filler.fill(param);
        String fileName = buildFileName(reportDefinition, "xlsx");
        response.setContentType("application/octet-stream;charset=ISO8859-1");
        fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
        response.setHeader("Content-Disposition", "attachment;filename=\"" + fileName + "\"");
        ExcelExporter excelExporter = new ExcelExporter();
        excelExporter.setExporterInput(new SimpleExporterInput(reportPrint));
        excelExporter.setExporterOutput(new SimpleStreamExporterOutput(response.getOutputStream()));
        excelExporter.exportReport();
    }

    private String buildFileName(ReportDefinition reportDefinition, String extName) {
        if (StrUtil.isNotBlank(reportDefinition.getDesc())) {
            return reportDefinition.getDesc() + "." + extName;
        } else {
            return DateUtil.formatDateNow() + "." + extName;
        }
    }


    @RequestMapping({"/runDataSet"})
    @ResponseBody
    public ResultMessage refreshDataSet(HttpServletRequest request) {
        String dataSetId = request.getParameter(ReportConstants.REPORT_DATA_SET_ID);
        Map<String, Object> parameters = RequestUtil.getRequestMap(request);
        Optional<ReportDataSetEntity> definition = reportDataSetService.findOne(dataSetId);
        if (definition.isPresent()) {
            DataSetDefinition dataSetDefinition = ((ReportDBDefManager) reportManager).createDataSetDefinition(ReportDataSetConverter.toDTO(definition.get()));
            QueryExecuteFactory queryExecuteFactory = ObjectFactory.getQueryExecuterFactory();
            QueryExecute queryExecuter = queryExecuteFactory.createQueryExecuter(dataSetDefinition, parameters);
            BRDataSource dataSource = queryExecuter.createBRDataSource();
            Map<String, List<Object>> datas = new HashMap<>();
            List<FieldDefinition> fields = dataSetDefinition.getFields();
            for (FieldDefinition field : fields) {
                String fieldName = field.getName();
                List<Object> data = new ArrayList();
                while (dataSource.next()) {
                    Object value = dataSource.getFieldValue(field.getFillFiled());
                    data.add(value);
                }
                datas.put(fieldName, data);
            }
            return Results.success(datas);
        } else {
            return Results.error("-1", "数据集不存在");
        }
    }


}